This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

#Load some useful libraries
library(limma)
library(DESeq2)
library(edgeR)
library(data.table)
library(tidyr)
library(magrittr)
library(tidyverse)
library(sjlabelled)
library(sjmisc)
library(ggplot2)
library(compositions)
library(broom)
library(DataCombine)
library(ggsignif)
library(ggpubr)
#import data
countdf <- read.csv(file = 'labs.csv')
rownames(countdf) <- NULL
meta <- read.csv(file = 'labs_metadata.csv')
rownames(meta) <- NULL
full <- read.csv(file = 'labs_full.csv')
rownames(full) <- NULL

#clean-up tables
#prepare for model
#make consistent index/column/label/ordering between tables/matrices
names(countdf)[1] <- 'name'
countdf[countdf==0] <- 0.0000001

# remove added X's from PCI names
names(countdf) <- sapply(str_remove_all(names(countdf),"X"),"[") 
count_matrix <- countdf
count_matrix <- count_matrix[,2:ncol(count_matrix)]
count_matrix <- as.matrix(count_matrix)
names(full) <- sapply(str_remove_all(colnames(full),"X"),"[")
names(count_matrix) <- sapply(str_remove_all(colnames(count_matrix),"X"),"[")

#factorize sex
meta$sex <- factor(meta$sex)
full$sex <- factor(full$sex)

#factorize BMF
meta$bowel <- factor(as.numeric(factor(meta$bowel, levels = c(1,2,3,4), labels = c(1,2,3,4))))
full$bowel <- factor(as.numeric(factor(full$bowel, levels = c(1,2,3,4), labels = c(1,2,3,4))))

#set reference category to be "High Normal" BMF (level = 3)
meta <- within(meta, bowel <- relevel(bowel, ref = 3))
full <- within(full, bowel <- relevel(bowel, ref = 3))

as.data.frame(count_matrix)
countdf <- DropNA(countdf)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
meta <- DropNA(meta)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
full <- DropNA(full)
No Var specified. Dropping all NAs from the data frame.

0 rows dropped from the data frame because of missing values.
countdf
meta
full
#design GLM using lmFit and eBayes
#Algorithm provided by and adapted from Christian Diener, PhD:
###########################################################################################################
design <- model.matrix(~bowel + sex + age + BMI_CALC + eGFR, full)
dge <- DGEList(counts=count_matrix)  # where `count_matrix` is the matrix mentioned above
logCPM <- cpm(dge, log=TRUE)  # takes the log of the data
fit <- lmFit(logCPM, design)  # fits the model for all labs
fit <- eBayes(fit)  # stabilizes the variances
###########################################################################################################
#Pre-process for plotting:

#Get results table for Constipation coefficient relative to High Normal BMF:
re_const <- topTable(fit, coef = 2, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 2
re_low <- topTable(fit, coef = 3, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 3
re_diarrhea <- topTable(fit, coef = 4, genelist = countdf$name, sort="p", number="none")  # Select the significant models by coefficient 4

indices <- match(rownames(re_const), rownames(meta)) # associate column of labs names with index of lab
re_const[1] <- countdf$name[indices] # associate the labs names with the labs indices
p_const <- re_const[re_const$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_const <- p_const[order(p_const$adj.P.Val),] # order by adj P value
p_const <- p_const[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns

#Get results table for Low Normal coefficient relative to High Normal BMF:
indices <- match(rownames(re_low), rownames(meta)) # associate column of labs names with index of lab
re_low[1] <- countdf$name[indices] # associate the labs names with the labs indices
p_low <- re_low[re_low$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_low <- p_low[order(p_low$adj.P.Val),] # order by adj P value
p_low <- p_low[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns

#Get results table for Diarrhea coefficient relative to High Normal BMF:
indices <- match(rownames(re_diarrhea), rownames(meta)) # associate column of labs names with index of lab
re_diarrhea[1] <- countdf$name[indices] # associate the labs names with the labs indices
p_diarrhea <- re_diarrhea[re_diarrhea$adj.P.Val < 0.05,] # create df of just significant adj P value results
p_diarrhea <- p_diarrhea[order(p_diarrhea$adj.P.Val),] # order by adj P value
p_diarrhea <- p_diarrhea[,c('logFC','B','adj.P.Val','ID','P.Value')] # keep only desired columns



#Show dfs of significant hits (there are none)
sig_const <- p_const[which(p_const$adj.P.Val < 0.05),]
sig_low <- p_low[which(p_low$adj.P.Val < 0.05),]
sig_diarrhea <- p_diarrhea[which(p_diarrhea$adj.P.Val < 0.05),]
sig_low$bowel <- 'Low Normal'
sig_low
sig_diarrhea
sig_const
#Main Text Figures
comparisons = list(c("Constipation","High Normal"),c("Low Normal","High Normal"),c("Diarrhea","High Normal"))
titles = list(c('OMEGA-6/OMEGA-3 RATIO'),c('EPA'),c('HOMOCYSTEINE, SERUM'),c('EOSINOPHILS'), c('BASOPHILS'))

myplots_main <- list()  # new empty list
#annotated only
labs_testanno <- cbind(full[,1],full[,6],as.data.frame(clr(full[,7:ncol(full)][,as.numeric(rownames(sig_low))])))
names(labs_testanno)[1] <- 'public_client_id'
names(labs_testanno)[2] <- 'bowel'
labs_testanno$bowel <- factor(labs_testanno$bowel, levels=c(1,2,3,4), labels = c("Constipation","Low Normal","High Normal","Diarrhea"))
labs_testanno$bowel <- factor(labs_testanno$bowel, levels=c("Constipation","Low Normal","High Normal","Diarrhea"), labels = c("Constipation","Low Normal","High Normal","Diarrhea"))

#Plotting:
#######################################################################
counter<<-1

# Annotation function:
sig = function(x){
  if(x < 0.001){"***"} 
  else if(x < 0.01){"**"}
  else if(x < 0.05){"*"}
  else{NA}}

# Special Graphing Function:
########################################
test = function(x,y,z,j) {
  x = NULL
  y = NULL
  
  temp<- names(labs_testanno)[[j+2]]
  temp_size <- merge(labs_testanno[,c(1,2,j+2)],labs_testanno[,c('public_client_id','bowel')])
  temp_size <- temp_size[,c('bowel',temp)]
  temp_size <- temp_size[which(!is.na(temp_size)),]
  temp_size <- length(levels(factor(temp_size$bowel)))
  
  if (counter==1) {
    results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Constipation'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Constipation'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  else if (counter==2) {
    results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Low Normal'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Low Normal'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  else {
     results = ifelse(nrow(sig_low[which(sig_low['bowel'] == 'Diarrhea'),]['adj.P.Val'])!=0,
           list(p.value = sig_low[which(sig_low['bowel'] == 'Diarrhea'),]['adj.P.Val'][[1]]),
           list(p.value = 1))
  }
  
  if (temp_size == 2) { #if the # of levels to the omic is 2 categories (incl High Normal)
    counter<<-1
    }
  
  if (temp_size == 3) { #if the # of levels to the omic is 3 categories (incl High Normal)
    counter<<-counter+1
    if (counter > 2) {counter<<-1}
  }
  
  if (temp_size == 4) { #if the # of levels to the omic is all 4 categories (incl High Normal)
    counter<<-counter+1
    if (counter > 3) {counter<<-1}
  }
  
  names(results) <- 'p.value'
  return(results)
}
##########################################

#for every significant hit, plot the graph for it and annotate it properly using the above function:
for (ind in 1:nrow(sig_low)) {
  y_name <- paste(names(labs_testanno)[c(ind+2)],sep="")
  myplots_main[[ind]] <- local({
    plotlim_loweranno = min(labs_testanno[[y_name]])
    plotlim_upperanno = max(labs_testanno[[y_name]])
    plotlim_baranno = plotlim_loweranno - 3.5
    plotlim_marginanno = plotlim_loweranno - 5
    labs_testanno <- labs_testanno[,c('bowel','public_client_id',y_name)]
    plt_anno <- ggplot(data = labs_testanno, aes(x = bowel, y = .data[[y_name]], group = bowel)) +
    scale_x_discrete(guide = guide_axis(n.dodge = 2))+
    geom_jitter(aes(color = bowel),  size = 0.1, cex = 0.05) +
    geom_boxplot(alpha=0.0,outlier.shape = NA) +
    theme(text = element_text(size = 9)) +
    ggtitle(label = str_wrap(titles[[ind]], width = 2)) +
    geom_signif(comparisons = comparisons, map_signif_level = sig, test = 'test', test.args = list(z = comparisons, j = ind), 
                y_position = plotlim_baranno, 
                step_increase = 0.15,  size = 0.5, 
                textsize = 1.5,
                tip_length = c(0,0)) +
    coord_cartesian(ylim=c(plotlim_loweranno,plotlim_upperanno),clip="off")+
    labs(color = "BMF Category", y = ifelse(ind == 1, "Log-Transformed\n Clinical Labs Level",""))+
    guides(colour = guide_legend(override.aes = list(size=7), title.position = 'left', nrow = 1, ncol = 4)) +
      
      
    theme(plot.margin = unit(c(0,0,abs(plotlim_marginanno),0), "cm"),
          legend.title = element_text(size=10), 
          legend.text = element_text(size=7),
          axis.text.x = element_blank(), 
          axis.title.x = element_blank()) +
      
    scale_fill_manual(limits = c("Constipation","Low Normal","High Normal","Diarrhea"), labels = c("Constipation","Low Normal","High Normal","Diarrhea"), values = colors(),
                    drop = FALSE)
  })
}
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
Warning: Duplicated aesthetics after name standardisation: size
#store the figures to save them:
counter <<- 1
figure_main <- ggarrange(plotlist = myplots_main[1:length(myplots_main)], labels = LETTERS[1:5], legend = "top", align = "hv", common.legend = TRUE, nrow = 1, ncol = 5)
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
Warning in if (x < 0.001) { :
  the condition has length > 1 and only the first element will be used
Warning: Removed 8 rows containing missing values (geom_signif).
#######################################################################
figure_main


#save the figures:
ggsave(
  "BMFvsLabsMain.png",
  plot = figure_main,
  device = NULL,
  path = NULL,
  scale = 1.2,
  width = NA,
  height = NA,
  units = c("in", "cm", "mm", "px"),
  dpi = 300,
  limitsize = TRUE,
  bg = NULL
)
Saving 8.24 x 5.09 in image

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiSmFtZXMgUC4gSm9obnNvbiAtIENsaW5pY2FsIEJsb29kIENoZW1pc3RyaWVzIExJTU1BIC0gdjMtNi0yMyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CiNMb2FkIHNvbWUgdXNlZnVsIGxpYnJhcmllcwpsaWJyYXJ5KGxpbW1hKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShlZGdlUikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzamxhYmVsbGVkKQpsaWJyYXJ5KHNqbWlzYykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNvbXBvc2l0aW9ucykKbGlicmFyeShicm9vbSkKbGlicmFyeShEYXRhQ29tYmluZSkKbGlicmFyeShnZ3NpZ25pZikKbGlicmFyeShnZ3B1YnIpCmBgYAoKCmBgYHtyfQojaW1wb3J0IGRhdGEKY291bnRkZiA8LSByZWFkLmNzdihmaWxlID0gJ2xhYnMuY3N2JykKcm93bmFtZXMoY291bnRkZikgPC0gTlVMTAptZXRhIDwtIHJlYWQuY3N2KGZpbGUgPSAnbGFic19tZXRhZGF0YS5jc3YnKQpyb3duYW1lcyhtZXRhKSA8LSBOVUxMCmZ1bGwgPC0gcmVhZC5jc3YoZmlsZSA9ICdsYWJzX2Z1bGwuY3N2JykKcm93bmFtZXMoZnVsbCkgPC0gTlVMTAoKI2NsZWFuLXVwIHRhYmxlcwojcHJlcGFyZSBmb3IgbW9kZWwKI21ha2UgY29uc2lzdGVudCBpbmRleC9jb2x1bW4vbGFiZWwvb3JkZXJpbmcgYmV0d2VlbiB0YWJsZXMvbWF0cmljZXMKbmFtZXMoY291bnRkZilbMV0gPC0gJ25hbWUnCmNvdW50ZGZbY291bnRkZj09MF0gPC0gMC4wMDAwMDAxCgojIHJlbW92ZSBhZGRlZCBYJ3MgZnJvbSBQQ0kgbmFtZXMKbmFtZXMoY291bnRkZikgPC0gc2FwcGx5KHN0cl9yZW1vdmVfYWxsKG5hbWVzKGNvdW50ZGYpLCJYIiksIlsiKSAKY291bnRfbWF0cml4IDwtIGNvdW50ZGYKY291bnRfbWF0cml4IDwtIGNvdW50X21hdHJpeFssMjpuY29sKGNvdW50X21hdHJpeCldCmNvdW50X21hdHJpeCA8LSBhcy5tYXRyaXgoY291bnRfbWF0cml4KQpuYW1lcyhmdWxsKSA8LSBzYXBwbHkoc3RyX3JlbW92ZV9hbGwoY29sbmFtZXMoZnVsbCksIlgiKSwiWyIpCm5hbWVzKGNvdW50X21hdHJpeCkgPC0gc2FwcGx5KHN0cl9yZW1vdmVfYWxsKGNvbG5hbWVzKGNvdW50X21hdHJpeCksIlgiKSwiWyIpCgojZmFjdG9yaXplIHNleAptZXRhJHNleCA8LSBmYWN0b3IobWV0YSRzZXgpCmZ1bGwkc2V4IDwtIGZhY3RvcihmdWxsJHNleCkKCiNmYWN0b3JpemUgQk1GCm1ldGEkYm93ZWwgPC0gZmFjdG9yKGFzLm51bWVyaWMoZmFjdG9yKG1ldGEkYm93ZWwsIGxldmVscyA9IGMoMSwyLDMsNCksIGxhYmVscyA9IGMoMSwyLDMsNCkpKSkKZnVsbCRib3dlbCA8LSBmYWN0b3IoYXMubnVtZXJpYyhmYWN0b3IoZnVsbCRib3dlbCwgbGV2ZWxzID0gYygxLDIsMyw0KSwgbGFiZWxzID0gYygxLDIsMyw0KSkpKQoKI3NldCByZWZlcmVuY2UgY2F0ZWdvcnkgdG8gYmUgIkhpZ2ggTm9ybWFsIiBCTUYgKGxldmVsID0gMykKbWV0YSA8LSB3aXRoaW4obWV0YSwgYm93ZWwgPC0gcmVsZXZlbChib3dlbCwgcmVmID0gMykpCmZ1bGwgPC0gd2l0aGluKGZ1bGwsIGJvd2VsIDwtIHJlbGV2ZWwoYm93ZWwsIHJlZiA9IDMpKQoKYXMuZGF0YS5mcmFtZShjb3VudF9tYXRyaXgpCmNvdW50ZGYgPC0gRHJvcE5BKGNvdW50ZGYpCm1ldGEgPC0gRHJvcE5BKG1ldGEpCmZ1bGwgPC0gRHJvcE5BKGZ1bGwpCmNvdW50ZGYKbWV0YQpmdWxsCmBgYAoKCmBgYHtyfQojZGVzaWduIEdMTSB1c2luZyBsbUZpdCBhbmQgZUJheWVzCiNBbGdvcml0aG0gcHJvdmlkZWQgYnkgYW5kIGFkYXB0ZWQgZnJvbSBDaHJpc3RpYW4gRGllbmVyLCBQaEQ6CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofmJvd2VsICsgc2V4ICsgYWdlICsgQk1JX0NBTEMgKyBlR0ZSLCBmdWxsKQpkZ2UgPC0gREdFTGlzdChjb3VudHM9Y291bnRfbWF0cml4KSAgIyB3aGVyZSBgY291bnRfbWF0cml4YCBpcyB0aGUgbWF0cml4IG1lbnRpb25lZCBhYm92ZQpsb2dDUE0gPC0gY3BtKGRnZSwgbG9nPVRSVUUpICAjIHRha2VzIHRoZSBsb2cgb2YgdGhlIGRhdGEKZml0IDwtIGxtRml0KGxvZ0NQTSwgZGVzaWduKSAgIyBmaXRzIHRoZSBtb2RlbCBmb3IgYWxsIGxhYnMKZml0IDwtIGVCYXllcyhmaXQpICAjIHN0YWJpbGl6ZXMgdGhlIHZhcmlhbmNlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpgYGAKCgpgYGB7cn0KI1ByZS1wcm9jZXNzIGZvciBwbG90dGluZzoKCiNHZXQgcmVzdWx0cyB0YWJsZSBmb3IgQ29uc3RpcGF0aW9uIGNvZWZmaWNpZW50IHJlbGF0aXZlIHRvIEhpZ2ggTm9ybWFsIEJNRjoKcmVfY29uc3QgPC0gdG9wVGFibGUoZml0LCBjb2VmID0gMiwgZ2VuZWxpc3QgPSBjb3VudGRmJG5hbWUsIHNvcnQ9InAiLCBudW1iZXI9Im5vbmUiKSAgIyBTZWxlY3QgdGhlIHNpZ25pZmljYW50IG1vZGVscyBieSBjb2VmZmljaWVudCAyCnJlX2xvdyA8LSB0b3BUYWJsZShmaXQsIGNvZWYgPSAzLCBnZW5lbGlzdCA9IGNvdW50ZGYkbmFtZSwgc29ydD0icCIsIG51bWJlcj0ibm9uZSIpICAjIFNlbGVjdCB0aGUgc2lnbmlmaWNhbnQgbW9kZWxzIGJ5IGNvZWZmaWNpZW50IDMKcmVfZGlhcnJoZWEgPC0gdG9wVGFibGUoZml0LCBjb2VmID0gNCwgZ2VuZWxpc3QgPSBjb3VudGRmJG5hbWUsIHNvcnQ9InAiLCBudW1iZXI9Im5vbmUiKSAgIyBTZWxlY3QgdGhlIHNpZ25pZmljYW50IG1vZGVscyBieSBjb2VmZmljaWVudCA0CgppbmRpY2VzIDwtIG1hdGNoKHJvd25hbWVzKHJlX2NvbnN0KSwgcm93bmFtZXMobWV0YSkpICMgYXNzb2NpYXRlIGNvbHVtbiBvZiBsYWJzIG5hbWVzIHdpdGggaW5kZXggb2YgbGFiCnJlX2NvbnN0WzFdIDwtIGNvdW50ZGYkbmFtZVtpbmRpY2VzXSAjIGFzc29jaWF0ZSB0aGUgbGFicyBuYW1lcyB3aXRoIHRoZSBsYWJzIGluZGljZXMKcF9jb25zdCA8LSByZV9jb25zdFtyZV9jb25zdCRhZGouUC5WYWwgPCAwLjA1LF0gIyBjcmVhdGUgZGYgb2YganVzdCBzaWduaWZpY2FudCBhZGogUCB2YWx1ZSByZXN1bHRzCnBfY29uc3QgPC0gcF9jb25zdFtvcmRlcihwX2NvbnN0JGFkai5QLlZhbCksXSAjIG9yZGVyIGJ5IGFkaiBQIHZhbHVlCnBfY29uc3QgPC0gcF9jb25zdFssYygnbG9nRkMnLCdCJywnYWRqLlAuVmFsJywnSUQnLCdQLlZhbHVlJyldICMga2VlcCBvbmx5IGRlc2lyZWQgY29sdW1ucwoKI0dldCByZXN1bHRzIHRhYmxlIGZvciBMb3cgTm9ybWFsIGNvZWZmaWNpZW50IHJlbGF0aXZlIHRvIEhpZ2ggTm9ybWFsIEJNRjoKaW5kaWNlcyA8LSBtYXRjaChyb3duYW1lcyhyZV9sb3cpLCByb3duYW1lcyhtZXRhKSkgIyBhc3NvY2lhdGUgY29sdW1uIG9mIGxhYnMgbmFtZXMgd2l0aCBpbmRleCBvZiBsYWIKcmVfbG93WzFdIDwtIGNvdW50ZGYkbmFtZVtpbmRpY2VzXSAjIGFzc29jaWF0ZSB0aGUgbGFicyBuYW1lcyB3aXRoIHRoZSBsYWJzIGluZGljZXMKcF9sb3cgPC0gcmVfbG93W3JlX2xvdyRhZGouUC5WYWwgPCAwLjA1LF0gIyBjcmVhdGUgZGYgb2YganVzdCBzaWduaWZpY2FudCBhZGogUCB2YWx1ZSByZXN1bHRzCnBfbG93IDwtIHBfbG93W29yZGVyKHBfbG93JGFkai5QLlZhbCksXSAjIG9yZGVyIGJ5IGFkaiBQIHZhbHVlCnBfbG93IDwtIHBfbG93WyxjKCdsb2dGQycsJ0InLCdhZGouUC5WYWwnLCdJRCcsJ1AuVmFsdWUnKV0gIyBrZWVwIG9ubHkgZGVzaXJlZCBjb2x1bW5zCgojR2V0IHJlc3VsdHMgdGFibGUgZm9yIERpYXJyaGVhIGNvZWZmaWNpZW50IHJlbGF0aXZlIHRvIEhpZ2ggTm9ybWFsIEJNRjoKaW5kaWNlcyA8LSBtYXRjaChyb3duYW1lcyhyZV9kaWFycmhlYSksIHJvd25hbWVzKG1ldGEpKSAjIGFzc29jaWF0ZSBjb2x1bW4gb2YgbGFicyBuYW1lcyB3aXRoIGluZGV4IG9mIGxhYgpyZV9kaWFycmhlYVsxXSA8LSBjb3VudGRmJG5hbWVbaW5kaWNlc10gIyBhc3NvY2lhdGUgdGhlIGxhYnMgbmFtZXMgd2l0aCB0aGUgbGFicyBpbmRpY2VzCnBfZGlhcnJoZWEgPC0gcmVfZGlhcnJoZWFbcmVfZGlhcnJoZWEkYWRqLlAuVmFsIDwgMC4wNSxdICMgY3JlYXRlIGRmIG9mIGp1c3Qgc2lnbmlmaWNhbnQgYWRqIFAgdmFsdWUgcmVzdWx0cwpwX2RpYXJyaGVhIDwtIHBfZGlhcnJoZWFbb3JkZXIocF9kaWFycmhlYSRhZGouUC5WYWwpLF0gIyBvcmRlciBieSBhZGogUCB2YWx1ZQpwX2RpYXJyaGVhIDwtIHBfZGlhcnJoZWFbLGMoJ2xvZ0ZDJywnQicsJ2Fkai5QLlZhbCcsJ0lEJywnUC5WYWx1ZScpXSAjIGtlZXAgb25seSBkZXNpcmVkIGNvbHVtbnMKCgoKI1Nob3cgZGZzIG9mIHNpZ25pZmljYW50IGhpdHMgKHRoZXJlIGFyZSBub25lKQpzaWdfY29uc3QgPC0gcF9jb25zdFt3aGljaChwX2NvbnN0JGFkai5QLlZhbCA8IDAuMDUpLF0Kc2lnX2xvdyA8LSBwX2xvd1t3aGljaChwX2xvdyRhZGouUC5WYWwgPCAwLjA1KSxdCnNpZ19kaWFycmhlYSA8LSBwX2RpYXJyaGVhW3doaWNoKHBfZGlhcnJoZWEkYWRqLlAuVmFsIDwgMC4wNSksXQpzaWdfbG93JGJvd2VsIDwtICdMb3cgTm9ybWFsJwpzaWdfbG93CnNpZ19kaWFycmhlYSAjIG5vIGhpdHMKc2lnX2NvbnN0ICMgbm8gaGl0cwpgYGAKCgoKYGBge3J9CiNNYWluIFRleHQgRmlndXJlcwpjb21wYXJpc29ucyA9IGxpc3QoYygiQ29uc3RpcGF0aW9uIiwiSGlnaCBOb3JtYWwiKSxjKCJMb3cgTm9ybWFsIiwiSGlnaCBOb3JtYWwiKSxjKCJEaWFycmhlYSIsIkhpZ2ggTm9ybWFsIikpCnRpdGxlcyA9IGxpc3QoYygnT01FR0EtNi9PTUVHQS0zIFJBVElPJyksYygnRVBBJyksYygnSE9NT0NZU1RFSU5FLCBTRVJVTScpLGMoJ0VPU0lOT1BISUxTJyksIGMoJ0JBU09QSElMUycpKQoKbXlwbG90c19tYWluIDwtIGxpc3QoKSAgIyBuZXcgZW1wdHkgbGlzdAojYW5ub3RhdGVkIG9ubHkKbGFic190ZXN0YW5ubyA8LSBjYmluZChmdWxsWywxXSxmdWxsWyw2XSxhcy5kYXRhLmZyYW1lKGNscihmdWxsWyw3Om5jb2woZnVsbCldWyxhcy5udW1lcmljKHJvd25hbWVzKHNpZ19sb3cpKV0pKSkKbmFtZXMobGFic190ZXN0YW5ubylbMV0gPC0gJ3B1YmxpY19jbGllbnRfaWQnCm5hbWVzKGxhYnNfdGVzdGFubm8pWzJdIDwtICdib3dlbCcKbGFic190ZXN0YW5ubyRib3dlbCA8LSBmYWN0b3IobGFic190ZXN0YW5ubyRib3dlbCwgbGV2ZWxzPWMoMSwyLDMsNCksIGxhYmVscyA9IGMoIkNvbnN0aXBhdGlvbiIsIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIsIkRpYXJyaGVhIikpCmxhYnNfdGVzdGFubm8kYm93ZWwgPC0gZmFjdG9yKGxhYnNfdGVzdGFubm8kYm93ZWwsIGxldmVscz1jKCJDb25zdGlwYXRpb24iLCJMb3cgTm9ybWFsIiwiSGlnaCBOb3JtYWwiLCJEaWFycmhlYSIpLCBsYWJlbHMgPSBjKCJDb25zdGlwYXRpb24iLCJMb3cgTm9ybWFsIiwiSGlnaCBOb3JtYWwiLCJEaWFycmhlYSIpKQoKI1Bsb3R0aW5nOgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpjb3VudGVyPDwtMQoKIyBBbm5vdGF0aW9uIGZ1bmN0aW9uOgpzaWcgPSBmdW5jdGlvbih4KXsKICBpZih4IDwgMC4wMDEpeyIqKioifSAKICBlbHNlIGlmKHggPCAwLjAxKXsiKioifQogIGVsc2UgaWYoeCA8IDAuMDUpeyIqIn0KICBlbHNle05BfX0KCiMgU3BlY2lhbCBHcmFwaGluZyBGdW5jdGlvbjoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwp0ZXN0ID0gZnVuY3Rpb24oeCx5LHosaikgewogIHggPSBOVUxMCiAgeSA9IE5VTEwKICAKICB0ZW1wPC0gbmFtZXMobGFic190ZXN0YW5ubylbW2orMl1dCiAgdGVtcF9zaXplIDwtIG1lcmdlKGxhYnNfdGVzdGFubm9bLGMoMSwyLGorMildLGxhYnNfdGVzdGFubm9bLGMoJ3B1YmxpY19jbGllbnRfaWQnLCdib3dlbCcpXSkKICB0ZW1wX3NpemUgPC0gdGVtcF9zaXplWyxjKCdib3dlbCcsdGVtcCldCiAgdGVtcF9zaXplIDwtIHRlbXBfc2l6ZVt3aGljaCghaXMubmEodGVtcF9zaXplKSksXQogIHRlbXBfc2l6ZSA8LSBsZW5ndGgobGV2ZWxzKGZhY3Rvcih0ZW1wX3NpemUkYm93ZWwpKSkKICAKICBpZiAoY291bnRlcj09MSkgewogICAgcmVzdWx0cyA9IGlmZWxzZShucm93KHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnQ29uc3RpcGF0aW9uJyksXVsnYWRqLlAuVmFsJ10pIT0wLAogICAgICAgICAgIGxpc3QocC52YWx1ZSA9IHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnQ29uc3RpcGF0aW9uJyksXVsnYWRqLlAuVmFsJ11bWzFdXSksCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gMSkpCiAgfQogIGVsc2UgaWYgKGNvdW50ZXI9PTIpIHsKICAgIHJlc3VsdHMgPSBpZmVsc2UobnJvdyhzaWdfbG93W3doaWNoKHNpZ19sb3dbJ2Jvd2VsJ10gPT0gJ0xvdyBOb3JtYWwnKSxdWydhZGouUC5WYWwnXSkhPTAsCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gc2lnX2xvd1t3aGljaChzaWdfbG93Wydib3dlbCddID09ICdMb3cgTm9ybWFsJyksXVsnYWRqLlAuVmFsJ11bWzFdXSksCiAgICAgICAgICAgbGlzdChwLnZhbHVlID0gMSkpCiAgfQogIGVsc2UgewogICAgIHJlc3VsdHMgPSBpZmVsc2UobnJvdyhzaWdfbG93W3doaWNoKHNpZ19sb3dbJ2Jvd2VsJ10gPT0gJ0RpYXJyaGVhJyksXVsnYWRqLlAuVmFsJ10pIT0wLAogICAgICAgICAgIGxpc3QocC52YWx1ZSA9IHNpZ19sb3dbd2hpY2goc2lnX2xvd1snYm93ZWwnXSA9PSAnRGlhcnJoZWEnKSxdWydhZGouUC5WYWwnXVtbMV1dKSwKICAgICAgICAgICBsaXN0KHAudmFsdWUgPSAxKSkKICB9CiAgCiAgaWYgKHRlbXBfc2l6ZSA9PSAyKSB7ICNpZiB0aGUgIyBvZiBsZXZlbHMgdG8gdGhlIG9taWMgaXMgMiBjYXRlZ29yaWVzIChpbmNsIEhpZ2ggTm9ybWFsKQogICAgY291bnRlcjw8LTEKICAgIH0KICAKICBpZiAodGVtcF9zaXplID09IDMpIHsgI2lmIHRoZSAjIG9mIGxldmVscyB0byB0aGUgb21pYyBpcyAzIGNhdGVnb3JpZXMgKGluY2wgSGlnaCBOb3JtYWwpCiAgICBjb3VudGVyPDwtY291bnRlcisxCiAgICBpZiAoY291bnRlciA+IDIpIHtjb3VudGVyPDwtMX0KICB9CiAgCiAgaWYgKHRlbXBfc2l6ZSA9PSA0KSB7ICNpZiB0aGUgIyBvZiBsZXZlbHMgdG8gdGhlIG9taWMgaXMgYWxsIDQgY2F0ZWdvcmllcyAoaW5jbCBIaWdoIE5vcm1hbCkKICAgIGNvdW50ZXI8PC1jb3VudGVyKzEKICAgIGlmIChjb3VudGVyID4gMykge2NvdW50ZXI8PC0xfQogIH0KICAKICBuYW1lcyhyZXN1bHRzKSA8LSAncC52YWx1ZScKICByZXR1cm4ocmVzdWx0cykKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiNmb3IgZXZlcnkgc2lnbmlmaWNhbnQgaGl0LCBwbG90IHRoZSBncmFwaCBmb3IgaXQgYW5kIGFubm90YXRlIGl0IHByb3Blcmx5IHVzaW5nIHRoZSBhYm92ZSBmdW5jdGlvbjoKZm9yIChpbmQgaW4gMTpucm93KHNpZ19sb3cpKSB7CiAgeV9uYW1lIDwtIHBhc3RlKG5hbWVzKGxhYnNfdGVzdGFubm8pW2MoaW5kKzIpXSxzZXA9IiIpCiAgbXlwbG90c19tYWluW1tpbmRdXSA8LSBsb2NhbCh7CiAgICBwbG90bGltX2xvd2VyYW5ubyA9IG1pbihsYWJzX3Rlc3Rhbm5vW1t5X25hbWVdXSkKICAgIHBsb3RsaW1fdXBwZXJhbm5vID0gbWF4KGxhYnNfdGVzdGFubm9bW3lfbmFtZV1dKQogICAgcGxvdGxpbV9iYXJhbm5vID0gcGxvdGxpbV9sb3dlcmFubm8gLSAzLjUKICAgIHBsb3RsaW1fbWFyZ2luYW5ubyA9IHBsb3RsaW1fbG93ZXJhbm5vIC0gNQogICAgbGFic190ZXN0YW5ubyA8LSBsYWJzX3Rlc3Rhbm5vWyxjKCdib3dlbCcsJ3B1YmxpY19jbGllbnRfaWQnLHlfbmFtZSldCiAgICBwbHRfYW5ubyA8LSBnZ3Bsb3QoZGF0YSA9IGxhYnNfdGVzdGFubm8sIGFlcyh4ID0gYm93ZWwsIHkgPSAuZGF0YVtbeV9uYW1lXV0sIGdyb3VwID0gYm93ZWwpKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGd1aWRlID0gZ3VpZGVfYXhpcyhuLmRvZGdlID0gMikpKwogICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gYm93ZWwpLCAgc2l6ZSA9IDAuMSwgY2V4ID0gMC4wNSkgKwogICAgZ2VvbV9ib3hwbG90KGFscGhhPTAuMCxvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpKSArCiAgICBnZ3RpdGxlKGxhYmVsID0gc3RyX3dyYXAodGl0bGVzW1tpbmRdXSwgd2lkdGggPSAyKSkgKwogICAgZ2VvbV9zaWduaWYoY29tcGFyaXNvbnMgPSBjb21wYXJpc29ucywgbWFwX3NpZ25pZl9sZXZlbCA9IHNpZywgdGVzdCA9ICd0ZXN0JywgdGVzdC5hcmdzID0gbGlzdCh6ID0gY29tcGFyaXNvbnMsIGogPSBpbmQpLCAKICAgICAgICAgICAgICAgIHlfcG9zaXRpb24gPSBwbG90bGltX2JhcmFubm8sIAogICAgICAgICAgICAgICAgc3RlcF9pbmNyZWFzZSA9IDAuMTUsICBzaXplID0gMC41LCAKICAgICAgICAgICAgICAgIHRleHRzaXplID0gMS41LAogICAgICAgICAgICAgICAgdGlwX2xlbmd0aCA9IGMoMCwwKSkgKwogICAgY29vcmRfY2FydGVzaWFuKHlsaW09YyhwbG90bGltX2xvd2VyYW5ubyxwbG90bGltX3VwcGVyYW5ubyksY2xpcD0ib2ZmIikrCiAgICBsYWJzKGNvbG9yID0gIkJNRiBDYXRlZ29yeSIsIHkgPSBpZmVsc2UoaW5kID09IDEsICJMb2ctVHJhbnNmb3JtZWRcbiBDbGluaWNhbCBMYWJzIExldmVsIiwiIikpKwogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NyksIHRpdGxlLnBvc2l0aW9uID0gJ2xlZnQnLCBucm93ID0gMSwgbmNvbCA9IDQpKSArCiAgICAgIAogICAgICAKICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCxhYnMocGxvdGxpbV9tYXJnaW5hbm5vKSwwKSwgImNtIiksCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwKICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKGxpbWl0cyA9IGMoIkNvbnN0aXBhdGlvbiIsIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIsIkRpYXJyaGVhIiksIGxhYmVscyA9IGMoIkNvbnN0aXBhdGlvbiIsIkxvdyBOb3JtYWwiLCJIaWdoIE5vcm1hbCIsIkRpYXJyaGVhIiksIHZhbHVlcyA9IGNvbG9ycygpLAogICAgICAgICAgICAgICAgICAgIGRyb3AgPSBGQUxTRSkKICB9KQp9Cgojc3RvcmUgdGhlIGZpZ3VyZXMgdG8gc2F2ZSB0aGVtOgpjb3VudGVyIDw8LSAxCmZpZ3VyZV9tYWluIDwtIGdnYXJyYW5nZShwbG90bGlzdCA9IG15cGxvdHNfbWFpblsxOmxlbmd0aChteXBsb3RzX21haW4pXSwgbGFiZWxzID0gTEVUVEVSU1sxOjVdLCBsZWdlbmQgPSAidG9wIiwgYWxpZ24gPSAiaHYiLCBjb21tb24ubGVnZW5kID0gVFJVRSwgbnJvdyA9IDEsIG5jb2wgPSA1KQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKZmlndXJlX21haW4KCiNzYXZlIHRoZSBmaWd1cmVzOgpnZ3NhdmUoCiAgIkJNRnZzTGFic01haW4ucG5nIiwKICBwbG90ID0gZmlndXJlX21haW4sCiAgZGV2aWNlID0gTlVMTCwKICBwYXRoID0gTlVMTCwKICBzY2FsZSA9IDEuMiwKICB3aWR0aCA9IE5BLAogIGhlaWdodCA9IE5BLAogIHVuaXRzID0gYygiaW4iLCAiY20iLCAibW0iLCAicHgiKSwKICBkcGkgPSAzMDAsCiAgbGltaXRzaXplID0gVFJVRSwKICBiZyA9IE5VTEwKKQpgYGAKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==